package store

import (
	"time"

	"git.oschina.net/antlinker/antevent/server/app"

	"log"

	"git.oschina.net/antlinker/antevent/server/engine"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

const (
	collNameEventList        = "eventList"     // 事件发生集合
	collNameEventHookHistory = "eventHookList" // 事件或掉历史集合
)

// AnalysisStore 事件分析
type AnalysisStore interface {
	// 查询事件
	QueryEvent(etype, action, from string, page int, count int) (result app.AntEventHistoryList, err error)
	// 查询事件监听执行情况
	QueryHook(eid string) (result app.AntEvenBackCollection, err error)
}

// CreateAnalysisStore 创建一个事件分析存储器
func CreateAnalysisStore(db MyMgoer) AnalysisStore {
	return &eventStore{db: db}
}

// CreateEventStore 创建一个事件存储器
func CreateEventStore(db MyMgoer) engine.EventStorer {
	return &eventStore{db: db}
}

// eventStore 数据存储
type eventStore struct {
	db MyMgoer
}

// 存储钩子回调事件失败
func (s eventStore) HookError(url string, ei engine.Event, err error, start, end time.Time) {
	s.hookSave(url, ei, start, end, false, err)
}

// 存储钩子回调事件成功
func (s eventStore) HookSuccess(url string, ei engine.Event, start, end time.Time) {
	s.hookSave(url, ei, start, end, true, nil)
}

// storeEvent 事件
type storeHistoryHook struct {
	Eid       string      `bson:"eid"`
	HookURL   string      `bson:"hookUrl"`
	ExecOk    bool        `bson:"execOk"`
	StartTime time.Time   `bson:"startTime"`
	EndTime   time.Time   `bson:"endTime"`
	HookTime  int64       `bson:"hookTime"`
	Error     string      `bson:"error,omitempty"`
	Data      interface{} `bson:"data"`
}

// 存储钩子回调事件成功
func (s eventStore) hookSave(url string, ei engine.Event, start, end time.Time, execok bool, execerr error) {
	err := s.db.ExecSync(collNameEventHookHistory, func(coll *mgo.Collection) error {
		q := storeHistoryHook{
			Eid:       ei.Eid,
			HookURL:   url,
			ExecOk:    execok,
			StartTime: start,
			EndTime:   end,
			HookTime:  int64(end.Sub(start)),
			Data:      ei.Data,
		}
		if !execok {
			q.Error = execerr.Error()
		}
		return coll.Insert(q)
	})
	if err != nil {
		log.Printf("HookSuccess存储失败:%v\n", err)
	}
}

// storeEvent 事件
type storeEvent struct {
	Eid     string      `bson:"_id"`
	Action  string      `bson:"action"`
	Etype   string      `bson:"etype"`
	From    string      `bson:"from"`
	OccTime time.Time   `bson:"occTime"`
	Data    interface{} `bson:"data"`
	db      MyMgoer
}

// 存储事件
func (s eventStore) SaveEvent(ei engine.Event) {
	err := s.db.ExecSync(collNameEventList, func(coll *mgo.Collection) error {
		return coll.Insert(storeEvent{
			Eid:     ei.Eid,
			Action:  ei.Action,
			Etype:   ei.Etype,
			From:    ei.From,
			OccTime: ei.OccTime,
			Data:    ei.Data,
		})
	})
	if err != nil {
		log.Printf("SaveEvent失败:%v\n", err)
	}
}

// 存储事件
func (s eventStore) QueryEvent(etype, action, from string, page int, count int) (result app.AntEventHistoryList, err error) {
	err = s.db.ExecSync(collNameEventList, func(coll *mgo.Collection) error {

		query := bson.M{}
		if etype != "" {
			query["etype"] = etype
		}
		if action != "" {
			query["action"] = action
		}
		if from != "" {
			query["from"] = from
		}
		n, err := coll.Find(query).Count()
		if err != nil {
			return err
		}
		result.Total = n
		if page < 1 {
			page = 1
		}
		if count < 0 {
			count = 5
		}
		if count > 100 {
			count = 100
		}
		start := (page - 1) * count
		var ei []storeEvent
		err = coll.Find(query).Sort("-occTime").Skip(start).Limit(count).All(&ei)

		if err != nil {
			log.Printf("SaveEvent失败:%v\n", err)
		}
		if len(ei) > 0 {
			result.List = make([]*app.AntHistoryInfo, len(ei), len(ei))
			for i, v := range ei {
				result.List[i] = &app.AntHistoryInfo{
					Eid:     v.Eid,
					Etype:   v.Etype,
					Action:  &v.Action,
					From:    v.From,
					Data:    &v.Data,
					OccTime: v.OccTime,
				}
			}
		}
		return err
	})
	return
}

// 存储钩子回调事件成功
func (s eventStore) QueryHook(eid string) (result app.AntEvenBackCollection, err error) {
	err = s.db.ExecSync(collNameEventHookHistory, func(coll *mgo.Collection) error {

		var hs []storeHistoryHook
		err := coll.Find(bson.M{"eid": eid}).All(&hs)
		if err != nil {
			return err
		}
		if len(hs) > 0 {
			result = make(app.AntEvenBackCollection, len(hs), len(hs))
			for i, v := range hs {
				result[i] = &app.AntEvenBack{
					Eid:       v.Eid,
					EndTime:   v.EndTime,
					Error:     &v.Error,
					ExecTime:  int(v.HookTime),
					Hookurl:   v.HookURL,
					StartTime: v.StartTime,
					Success:   v.ExecOk,
				}
			}
		}
		return nil
	})
	if err != nil {
		log.Printf("HookSuccess存储失败:%v\n", err)
	}
	return
}
